package cn.lblbc.downloadlib;

import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * 分片文件下载器
 */
public class ChunkedFileDownloader {
    private static final String TAG = "ChunkedFileDownloader";
    private final OkHttpClient mHttpClient;
    private final ExecutorService mExecutorService;

    public ChunkedFileDownloader() {
        mHttpClient = new OkHttpClient();
        mExecutorService = Executors.newFixedThreadPool(4);
    }

    /**
     * 分片下载文件
     * @param fileInfo 文件信息
     * @param chunks 分片列表
     * @param callback 下载回调
     */
    public void downloadFileWithChunks(DownloadFileInfo fileInfo, List<DownloadChunk> chunks, ChunkDownloadCallback callback) {
        Log.d(TAG, "downloadFileWithChunks: Starting chunked download for file: " + fileInfo.getFileName() + 
            " with " + chunks.size() + " chunks");
        
        if (chunks.isEmpty()) {
            callback.onError(fileInfo, new IllegalArgumentException("No chunks to download"));
            return;
        }

        // 创建临时文件
        String tempFilePath = fileInfo.getLocalPath() + ".tmp";
        File tempFile = new File(tempFilePath);
        try {
            if (!tempFile.exists()) {
                File parentDir = tempFile.getParentFile();
                if (parentDir != null && !parentDir.exists()) {
                    parentDir.mkdirs();
                }
                tempFile.createNewFile();
            }
        } catch (IOException e) {
            Log.e(TAG, "downloadFileWithChunks: Failed to create temp file", e);
            callback.onError(fileInfo, e);
            return;
        }

        CountDownLatch latch = new CountDownLatch(chunks.size());
        List<Exception> errors = new ArrayList<>();
        long[] downloadedBytesArray = new long[chunks.size()];
        
        // 并发下载所有分片
        for (int i = 0; i < chunks.size(); i++) {
            final int index = i;
            final DownloadChunk chunk = chunks.get(i);
            mExecutorService.execute(() -> {
                try {
                    downloadChunk(fileInfo.getFileUrl(), tempFilePath, chunk, new ChunkDownloadListener() {
                        @Override
                        public void onProgress(long downloadedBytes) {
                            downloadedBytesArray[index] = downloadedBytes;
                            // 计算总进度
                            long totalDownloaded = 0;
                            for (long bytes : downloadedBytesArray) {
                                totalDownloaded += bytes;
                            }
                            long totalSize = fileInfo.getFileSize();
                            if (totalSize > 0) {
                                int progress = (int) ((totalDownloaded * 100) / totalSize);
                                callback.onProgress(progress, totalDownloaded, totalSize);
                            }
                        }

                        @Override
                        public void onSuccess() {
                            Log.d(TAG, "downloadChunk: Chunk downloaded successfully: " + chunk.getChunkId());
                            latch.countDown();
                        }

                        @Override
                        public void onError(Exception error) {
                            Log.e(TAG, "downloadChunk: Error downloading chunk: " + chunk.getChunkId(), error);
                            synchronized (errors) {
                                errors.add(error);
                            }
                            latch.countDown();
                        }
                    }, null);
                } catch (Exception e) {
                    Log.e(TAG, "downloadFileWithChunks: Error submitting chunk for download: " + chunk.getChunkId(), e);
                    synchronized (errors) {
                        errors.add(e);
                    }
                    latch.countDown();
                }
            });
        }

        // 等待所有分片下载完成
        new Thread(() -> {
            try {
                latch.await();
                if (errors.isEmpty()) {
                    // 合并临时文件到最终文件
                    File finalFile = new File(fileInfo.getLocalPath());
                    if (finalFile.exists()) {
                        finalFile.delete();
                    }
                    
                    Log.d(TAG, "downloadFileWithChunks: All chunks downloaded, renaming temp file to final file");
                    if (tempFile.renameTo(finalFile)) {
                        Log.d(TAG, "downloadFileWithChunks: File downloaded successfully: " + fileInfo.getFileName());
                        callback.onSuccess(fileInfo);
                    } else {
                        Log.e(TAG, "downloadFileWithChunks: Failed to rename temp file to final file");
                        callback.onError(fileInfo, new IOException("Failed to rename temp file"));
                    }
                } else {
                    Log.e(TAG, "downloadFileWithChunks: Some chunks failed to download, errors: " + errors.size());
                    callback.onError(fileInfo, errors.get(0)); // 返回第一个错误
                }
            } catch (InterruptedException e) {
                Log.e(TAG, "downloadFileWithChunks: Interrupted while waiting for chunks", e);
                Thread.currentThread().interrupt();
                callback.onError(fileInfo, e);
            }
        }).start();
    }
    
    /**
     * 分片下载文件（支持暂停）
     * @param fileInfo 文件信息
     * @param chunks 分片列表
     * @param callback 下载回调
     * @param pauseChecker 暂停检查器
     */
    public void downloadFileWithChunks(DownloadFileInfo fileInfo, List<DownloadChunk> chunks, ChunkDownloadCallback callback, PauseChecker pauseChecker) {
        Log.d(TAG, "downloadFileWithChunks: Starting chunked download for file: " + fileInfo.getFileName() + 
            " with " + chunks.size() + " chunks");
        
        if (chunks.isEmpty()) {
            callback.onError(fileInfo, new IllegalArgumentException("No chunks to download"));
            return;
        }

        // 创建临时文件
        String tempFilePath = fileInfo.getLocalPath() + ".tmp";
        File tempFile = new File(tempFilePath);
        try {
            if (!tempFile.exists()) {
                File parentDir = tempFile.getParentFile();
                if (parentDir != null && !parentDir.exists()) {
                    parentDir.mkdirs();
                }
                tempFile.createNewFile();
            }
        } catch (IOException e) {
            Log.e(TAG, "downloadFileWithChunks: Failed to create temp file", e);
            callback.onError(fileInfo, e);
            return;
        }

        CountDownLatch latch = new CountDownLatch(chunks.size());
        List<Exception> errors = new ArrayList<>();
        long[] downloadedBytesArray = new long[chunks.size()];
        
        // 并发下载所有分片
        for (int i = 0; i < chunks.size(); i++) {
            final int index = i;
            final DownloadChunk chunk = chunks.get(i);
            mExecutorService.execute(() -> {
                try {
                    downloadChunk(fileInfo.getFileUrl(), tempFilePath, chunk, new ChunkDownloadListener() {
                        @Override
                        public void onProgress(long downloadedBytes) {
                            // 检查是否需要暂停
                            if (pauseChecker != null) {
                                while (pauseChecker.isPaused()) {
                                    try {
                                        Thread.sleep(100);
                                    } catch (InterruptedException e) {
                                        Log.d(TAG, "downloadChunk: Thread interrupted while paused");
                                        Thread.currentThread().interrupt();
                                        // 如果线程被中断，通知监听器错误
                                        // 由于我们无法直接访问listener，我们抛出异常让外层处理
                                        throw new RuntimeException(new IOException("Download interrupted", e));
                                    }
                                }
                            }
                            
                            downloadedBytesArray[index] = downloadedBytes;
                            // 计算总进度
                            long totalDownloaded = 0;
                            for (long bytes : downloadedBytesArray) {
                                totalDownloaded += bytes;
                            }
                            long totalSize = fileInfo.getFileSize();
                            if (totalSize > 0) {
                                int progress = (int) ((totalDownloaded * 100) / totalSize);
                                callback.onProgress(progress, totalDownloaded, totalSize);
                            }
                        }

                        @Override
                        public void onSuccess() {
                            Log.d(TAG, "downloadChunk: Chunk downloaded successfully: " + chunk.getChunkId());
                            latch.countDown();
                        }

                        @Override
                        public void onError(Exception error) {
                            Log.e(TAG, "downloadChunk: Error downloading chunk: " + chunk.getChunkId(), error);
                            synchronized (errors) {
                                errors.add(error);
                            }
                            latch.countDown();
                        }
                    }, pauseChecker);
                } catch (Exception e) {
                    Log.e(TAG, "downloadFileWithChunks: Error submitting chunk for download: " + chunk.getChunkId(), e);
                    // 检查是否是由于线程中断引起的异常
                    if (e instanceof RuntimeException && e.getCause() instanceof IOException) {
                        synchronized (errors) {
                            errors.add((IOException) e.getCause());
                        }
                    } else {
                        synchronized (errors) {
                            errors.add(e);
                        }
                    }
                    latch.countDown();
                }
            });
        }

        // 等待所有分片下载完成
        new Thread(() -> {
            try {
                latch.await();
                if (errors.isEmpty()) {
                    // 合并临时文件到最终文件
                    File finalFile = new File(fileInfo.getLocalPath());
                    if (finalFile.exists()) {
                        finalFile.delete();
                    }
                    
                    Log.d(TAG, "downloadFileWithChunks: All chunks downloaded, renaming temp file to final file");
                    if (tempFile.renameTo(finalFile)) {
                        Log.d(TAG, "downloadFileWithChunks: File downloaded successfully: " + fileInfo.getFileName());
                        callback.onSuccess(fileInfo);
                    } else {
                        Log.e(TAG, "downloadFileWithChunks: Failed to rename temp file to final file");
                        callback.onError(fileInfo, new IOException("Failed to rename temp file"));
                    }
                } else {
                    Log.e(TAG, "downloadFileWithChunks: Some chunks failed to download, errors: " + errors.size());
                    callback.onError(fileInfo, errors.get(0)); // 返回第一个错误
                }
            } catch (InterruptedException e) {
                Log.e(TAG, "downloadFileWithChunks: Interrupted while waiting for chunks", e);
                Thread.currentThread().interrupt();
                callback.onError(fileInfo, e);
            }
        }).start();
    }

    /**
     * 下载单个分片
     * @param fileUrl 文件URL
     * @param tempFilePath 临时文件路径
     * @param chunk 分片信息
     * @param listener 下载监听器
     * @throws IOException IO异常
     */
    private void downloadChunk(String fileUrl, String tempFilePath, DownloadChunk chunk, ChunkDownloadListener listener, PauseChecker pauseChecker) throws IOException {
        Log.d(TAG, "downloadChunk: Starting download for chunk: " + chunk.getChunkId() + 
            " range: " + chunk.getStartPos() + "-" + chunk.getEndPos());
        
        // 计算还需要下载的字节数
        long remainingBytes = (chunk.getEndPos() - chunk.getStartPos() + 1) - chunk.getDownloadedSize();
        
        if (remainingBytes <= 0) {
            // 分片已经下载完成
            Log.d(TAG, "downloadChunk: Chunk already completed: " + chunk.getChunkId());
            listener.onSuccess();
            return;
        }
        
        // 构造Range请求头，从已下载的位置继续下载
        long startByte = chunk.getStartPos() + chunk.getDownloadedSize();
        String rangeHeader = "bytes=" + startByte + "-" + chunk.getEndPos();
        
        Log.d(TAG, "downloadChunk: Downloading chunk with range: " + rangeHeader);
        
        // 创建请求
        Request request = new Request.Builder()
                .url(fileUrl)
                .header("Range", rangeHeader)
                .build();
        
        // 发起请求
        Response response = mHttpClient.newCall(request).execute();
        if (!response.isSuccessful()) {
            throw new IOException("Failed to download chunk: " + response.code() + " " + response.message());
        }
        
        // 写入文件
        writeResponseToFile(response, tempFilePath, chunk.getStartPos() + chunk.getDownloadedSize(), listener, pauseChecker);
    }
    
    /**
     * 将响应数据写入文件指定位置
     * @param response HTTP响应
     * @param filePath 文件路径
     * @param startPosition 写入起始位置
     * @param listener 下载监听器
     * @throws IOException IO异常
     */
    private void writeResponseToFile(Response response, String filePath, long startPosition, ChunkDownloadListener listener, PauseChecker pauseChecker) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
        
        try (InputStream inputStream = response.body().byteStream();
             RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw")) {
            
            // 定位到写入位置
            randomAccessFile.seek(startPosition);
            
            byte[] buffer = new byte[8192];
            int bytesRead;
            long totalBytesRead = 0;
            
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                // 检查是否需要暂停
                if (pauseChecker != null) {
                    while (pauseChecker.isPaused()) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            throw new IOException("Download interrupted", e);
                        }
                    }
                }
                
                randomAccessFile.write(buffer, 0, bytesRead);
                totalBytesRead += bytesRead;
                listener.onProgress(totalBytesRead);
            }
            
            listener.onSuccess();
        }
    }

    /**
     * 分片下载监听器
     */
    private interface ChunkDownloadListener {
        /**
         * 下载进度回调
         * @param downloadedBytes 已下载字节数
         */
        void onProgress(long downloadedBytes);
        
        /**
         * 下载成功回调
         */
        void onSuccess();
        
        /**
         * 下载失败回调
         * @param error 错误信息
         */
        void onError(Exception error);
    }

    /**
     * 分片下载回调接口
     */
    public interface ChunkDownloadCallback {
        /**
         * 下载进度回调
         * @param progress 进度百分比
         * @param downloadedBytes 已下载字节数
         * @param totalBytes 总字节数
         */
        void onProgress(int progress, long downloadedBytes, long totalBytes);
        
        /**
         * 下载成功回调
         * @param fileInfo 文件信息
         */
        void onSuccess(DownloadFileInfo fileInfo);
        
        /**
         * 下载失败回调
         * @param fileInfo 文件信息
         * @param error 错误信息
         */
        void onError(DownloadFileInfo fileInfo, Exception error);
    }
    
    /**
     * 暂停检查器接口
     */
    public interface PauseChecker {
        boolean isPaused();
    }
}